<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>大文件上传</title>
    <head th:include="/base::base"><title></title></head>
    <link href="upload.css" rel="stylesheet" type="text/css">
</head>
<body>

<div id="app">
    <uploader
            ref="uploader"
            :options="options"
            :autoStart="false"
            :file-status-text="fileStatusText"
            @file-added="onFileAdded"
            @file-success="onFileSuccess"
            @file-error="onFileError"
            @file-progress="onFileProgress"
            class="uploader-example"
    >
        Author：@小阳仁
        <uploader-unsupport></uploader-unsupport>
        <uploader-drop>
            <p>拖动文件到这里上传</p>
            <uploader-btn>选择文件</uploader-btn>
        </uploader-drop>
        <uploader-list>
            <el-collapse v-model="activeName" accordion>
                <el-collapse-item title="文件列表" name="1">
                    <ul class="file-list">
                        <li v-for="file in uploadFileList" :key="file.id">
                            <uploader-file :file="file" :list="true" ref="uploaderFile">
                                <div slot-scope="props" style="display: flex;align-items: center;height: 100%;">
                                    <el-progress
                                            style="width: 85%"
                                            :stroke-width="18"
                                            :show-text="true"
                                            :text-inside="true"
                                            :format="e=> showDetail(e,props)"
                                            :percentage="percentage(props)"
                                            :color="e=>progressColor(e,props)">
                                    </el-progress>
                                    <el-button :icon="icon" circle v-if="props.paused || props.isUploading"
                                               @click="pause(file)" size="mini"></el-button>
                                    <el-button icon="el-icon-close" circle @click="remove(file)"
                                               size="mini"></el-button>
                                    <el-button icon="el-icon-download" circle v-if="props.isComplete"
                                               @click="download(file)"
                                               size="mini"></el-button>
                                </div>
                            </uploader-file>
                        </li>
                        <div class="no-file" v-if="!uploadFileList.length">
                            <i class="icon icon-empty-file"></i> 暂无待上传文件
                        </div>
                    </ul>
                </el-collapse-item>
            </el-collapse>
        </uploader-list>
    </uploader>
</div>
<script>
    // 分片大小，20MB
    const CHUNK_SIZE = 20 * 1024 * 1024;
    new Vue({
        el: '#app',
        data() {
            return {
                options: {
                    // 上传地址
                    target: "/fileStorage/upload",
                    // 是否开启服务器分片校验。默认为 true
                    testChunks: true,
                    // 真正上传的时候使用的 HTTP 方法,默认 POST
                    uploadMethod: "post",
                    // 分片大小
                    chunkSize: CHUNK_SIZE,
                    // 并发上传数，默认为 3
                    simultaneousUploads: 3,
                    /**
                     * 判断分片是否上传，秒传和断点续传基于此方法
                     * 这里根据实际业务来 用来判断哪些片已经上传过了 不用再重复上传了 [这里可以用来写断点续传！！！]
                     */
                    checkChunkUploadedByResponse: (chunk, message) => {
                        console.log("message", message)
                        // message是后台返回
                        let messageObj = JSON.parse(message);
                        let dataObj = messageObj.data;
                        if (dataObj.uploaded !== null) {
                            return dataObj.uploaded;
                        }
                        // 判断文件或分片是否已上传，已上传返回 true
                        // 这里的 uploadedChunks 是后台返回
                        return (dataObj.uploadedChunks || []).indexOf(chunk.offset + 1) >= 0;
                    },
                    parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) {
                        //格式化时间
                        return parsedTimeRemaining
                            .replace(/\syears?/, "年")
                            .replace(/\days?/, "天")
                            .replace(/\shours?/, "小时")
                            .replace(/\sminutes?/, "分钟")
                            .replace(/\sseconds?/, "秒");
                    },
                },
                // 修改上传状态
                fileStatusTextObj: {
                    success: "上传成功",
                    error: "上传错误",
                    uploading: "正在上传",
                    paused: "停止上传",
                    waiting: "等待中",
                },
                uploadFileList: [],
                collapse: true,
                activeName: 1,
                icon: `el-icon-video-pause`
            }
        },
        methods: {
            onFileAdded(file, event) {
                console.log("eeeee",event)
                // event.preventDefault();
                this.uploadFileList.push(file);
                console.log("file :>> ", file);
                // 有时 fileType为空，需截取字符
                console.log("文件类型：" + file.fileType + "文件大小：" + file.size + "B");
                // 1. todo 判断文件类型是否允许上传
                // 2. 计算文件 MD5 并请求后台判断是否已上传，是则取消上传
                console.log("校验MD5");
                this.getFileMD5(file, (md5) => {
                    if (md5 !== "") {
                        // 修改文件唯一标识
                        file.uniqueIdentifier = md5;
                        // 请求后台判断是否上传
                        // 恢复上传
                        file.resume();
                    }
                });
            },
            onFileSuccess(rootFile, file, response, chunk) {
                console.log("上传成功", rootFile, file, response, chunk);
                // 这里可以做一些上传成功之后的事情，比如，如果后端需要合并的话，可以通知到后端合并
            },
            onFileError(rootFile, file, message, chunk) {
                console.log("上传出错：" + message, rootFile, file, message, chunk);
            },
            onFileProgress(rootFile, file, chunk) {
                console.log(`当前进度：${Math.ceil(file._prevProgress * 100)}%`);
            },

            // 计算文件的MD5值
            getFileMD5(file, callback) {
                let spark = new SparkMD5.ArrayBuffer();
                let fileReader = new FileReader();
                //获取文件分片对象（注意它的兼容性，在不同浏览器的写法不同）
                let blobSlice =
                    File.prototype.slice ||
                    File.prototype.mozSlice ||
                    File.prototype.webkitSlice;
                // 当前分片下标
                let currentChunk = 0;
                // 分片总数(向下取整)
                let chunks = Math.ceil(file.size / CHUNK_SIZE);
                // MD5加密开始时间
                let startTime = new Date().getTime();
                // 暂停上传
                file.pause();
                loadNext();
                // fileReader.readAsArrayBuffer操作会触发onload事件
                fileReader.onload = function (e) {
                    // console.log("currentChunk :>> ", currentChunk);
                    spark.append(e.target.result);
                    if (currentChunk < chunks) {
                        currentChunk++;
                        loadNext();
                    } else {
                        // 该文件的md5值
                        let md5 = spark.end();
                        console.log(
                            `MD5计算完毕：${md5}，耗时：${new Date().getTime() - startTime} ms.`
                        );
                        // 回调传值md5
                        callback(md5);
                    }
                };
                fileReader.onerror = function () {
                    this.$message.error("文件读取错误");
                    file.cancel();
                };

                // 加载下一个分片
                function loadNext() {
                    const start = currentChunk * CHUNK_SIZE;
                    const end =
                        start + CHUNK_SIZE >= file.size ? file.size : start + CHUNK_SIZE;
                    // 文件分片操作，读取下一分片(fileReader.readAsArrayBuffer操作会触发onload事件)
                    fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
                }
            },
            fileStatusText(status, response) {
                if (status === "md5") {
                    return "校验MD5";
                } else {
                    return this.fileStatusTextObj[status];
                }
            },

            // 点击暂停
            pause(file, id) {
                console.log("file :>> ", file);
                if (file.paused) {
                    file.resume();
                    this.icon = 'el-icon-video-pause'
                } else {
                    this.icon = 'el-icon-video-play'
                    file.pause();
                }
            },
            // 点击删除

            remove(file) {
                this.uploadFileList.findIndex((item, index) => {
                    if (item.id === file.id) {
                        this.$nextTick(() => {
                            this.uploadFileList.splice(index, 1);
                        });
                    }
                });
            },
            showDetail(percentage, props) {
                let fileName = props.file.name;
                let isComplete = props.isComplete
                let formatUpload = this.formatFileSize(props.uploadedSize, 2);
                let fileSize = `${props.formatedSize}`;
                let timeRemaining = !isComplete ? ` 剩余时间:${props.formatedTimeRemaining}` : ''
                let uploaded = !isComplete ? ` 已上传:${formatUpload} / ${fileSize}` : ` 大小:${fileSize}`
                let speed = !isComplete ? ` 速度:${props.formatedAverageSpeed}` : ''
                if (props.error) {
                    return `${fileName} \t 上传失败`
                }
                return `${fileName} \t ${speed}  \t ${uploaded}  \t ${timeRemaining} \t  进度:${percentage} %`;
            },
            // 显示进度
            percentage(props) {
                let progress = props.progress.toFixed(2) * 100;
                return progress - 1 < 0 ? 0 : progress;
            },
            // 控制下进度条的颜色 ，异常的情况下显示红色
            progressColor(e, props) {
                if (props.error) {
                    return `#f56c6c`
                }
                if (e > 0) {
                    return `#1989fa`
                }
            },
            // 点击下载
            download(file, id) {
                console.log("file:>> ", file);
                window.location.href = `/fileStorage/download/${file.uniqueIdentifier}`;
            },
            formatFileSize(bytes, decimalPoint = 2) {
                if (bytes == 0) return "0 Bytes";
                let k = 1000,
                    sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
                    i = Math.floor(Math.log(bytes) / Math.log(k));
                return (
                    parseFloat((bytes / Math.pow(k, i)).toFixed(decimalPoint)) + " " + sizes[i]
                );
            }

        },
    })
</script>
</body>
</html>
